
/*
 * (a)  (C) 1990 by Adobe Systems Incorporated. All rights reserved.
 *
 * (b)  If this Sample Code is distributed as part of the Display PostScript
 *	System Software Development Kit from Adobe Systems Incorporated,
 *	then this copy is designated as Development Software and its use is
 *	subject to the terms of the License Agreement attached to such Kit.
 *
 * (c)  If this Sample Code is distributed independently, then the following
 *	terms apply:
 *
 * (d)  This file may be freely copied and redistributed as long as:
 *	1) Parts (a), (d), (e) and (f) continue to be included in the file,
 *	2) If the file has been modified in any way, a notice of such
 *      modification is conspicuously indicated.
 *
 * (e)  PostScript, Display PostScript, and Adobe are registered trademarks of
 *	Adobe Systems Incorporated.
 * 
 * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
 *	CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
 *	AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
 *	ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
 *	OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
 *	WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
 *	WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
 *	DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, 
 *	FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
 *	OF THIRD PARTY RIGHTS.
 */

/*
 *	Pattern.m
 *
 *	This class provides objects that correspond to patterns. It is modeled after the 
 *	Font class. It not a rigorous definition but is just a vehicle for demostrating the
 *	PostScript aspects of handling patterns.
 *
 *	Version:	2.0
 *	Author:	Ken Fromm
 *	History:
 *			03-07-91		Added this comment.
 */

#import "Pattern.h"
#import "PSWpatterns.h"
#import "PSWpatternsdemo.h"
#import "PSWsamples.h"

#import "PatternApp.h"

#import <appkit/Font.h>
#import <appkit/Matrix.h>
#import <appkit/Panel.h>
#import <appkit/View.h>
#import <appkit/nextstd.h>

#import <objc/hashtable.h>
#import <dpsclient/wraps.h>
#import <dpsclient/dpsclient.h>

@implementation Pattern : Object

+ initialize
{
	PSWPatternDefs();
	PSWPatternDemoDefs();

	PSWPatternOctagon();
	PSWPatternCircleStar();
	PSWPatternWeave();
	PSWPatternBrick();

	return self;
}

+ newPattern:(const char *) patternName  size:(float) patternSize
{
	return [Pattern  newPattern:patternName  size:patternSize  matrix:NX_IDENTITYMATRIX];
}

+ newPattern:(const char *) patternName  size:(float) patternSize  matrix:(const float *) patternMatrix
{
	self = [super  new];

	name = NXCopyStringBuffer(patternName);
	size = patternSize;
	if (name && size)
	{
		if (patternMatrix == NX_IDENTITYMATRIX)
			pFlags._matrixIsIdentity = YES;
		else if (patternMatrix == NX_FLIPPEDMATRIX)
			pFlags._matrixIsFlipped = YES;
		else
			matrix = (float *) patternMatrix;
	}

	return self;
}

- free
{
	[image  free];
	PSundefineuserobject(patternNum);
	PSundefineuserobject(patternImageNum);
	
	return [super  free];
}

/*
*	This method is only here to vary the number of pattern
*	images in a cell in order to generate the different test cases.
*	In a real implementation, one value would be chosen
*	as the optimal number of images per cell and to use this
*	value in the +new methods to define the pattern there. 
*
*	Twice the width and height plus an additional number is used
*	for the window size. The first cell is drawn at the origin of the
*	image window. In many cases, part of this cell will be drawn outside
*	the window resulting in an imcomplete image. The second cell
*	is drawn entirely within the window. It is that cell that is used for
*	compositing. Two time the width and height provides the room for
*	the second cell. The 2 adds a little spacing so that the image does
*	not lie on the edge of the window boundary.
*
*	This type of manipulation is necessary because patterns
*	are locked down to device space which in this case is window
*	space.
*/	
- definePatternRows:(int) rows  andCols:(int) cols
{
	volatile BOOL	ok = NO;

	int		available;

	float		r, g, b;

	float		aMatrix[6];

	float		*aMatrixPtr;

	NXRect	imageRect;

	if (name && size && rows && cols)
	{
		if (pFlags._matrixIsIdentity || pFlags._matrixIsFlipped || !matrix)
		{
			bzero(aMatrix, sizeof(aMatrix));
			aMatrix[0] = size;
			if (pFlags._matrixIsFlipped)
				aMatrix[3] = -size;
			else
				aMatrix[3] = size;
			aMatrixPtr = aMatrix;
		}
		else
			aMatrixPtr = matrix;

		NX_DURING
			PSWCheckPattern(name, &available);
			if (available)
			{
				/********************************************************/
				/*
				*	Only used to give the compositing image the current color.
				*	Unnecessary in a real implementation of patterns.
				*/
				PScurrentrgbcolor(&r, &g, &b);

				/*
				*	If locking to window, then forego the PatternViewDict.
				*/
				PSWBeginPattern();
				PSWBeginPatternView();
					PSWExpandPattern(name, aMatrixPtr, rows, cols);
				PSend();
				PSend();
				patternNum = DPSDefineUserObject(0);

				/********************************************************/
				/*
				*	This portion is used to get the image for compositing.
				*	The composite approach is not recommended for a
				*	real implementation and so this portion is unnecessary.
				*/
				if (patternNum)
				{
					PSWBeginPattern();
					PSWSizeImage(patternNum, &cellRect.origin.x,
						&cellRect.origin.y, &cellRect.size.width,
						&cellRect.size.height);
					PSend();

					imageRect = cellRect;
					imageRect.size.width = imageRect.size.width * 2 + 2;
					imageRect.size.height = imageRect.size.height * 2 + 2;
					image = [Panel  newContent:&imageRect
						style:NX_TITLEDSTYLE
						backing:NX_RETAINED
						buttonMask:NX_CLOSEBUTTONMASK
						defer:NO];
					[[image  contentView]  setFlipped:NO];

					[[image  contentView]  lockFocus];
						PSsetrgbcolor(r, g, b);
						PSWBeginPattern();
							PSWMakeImage(patternNum);
						PSend();
						patternImageNum = DPSDefineUserObject(0);
					[[image  contentView]  unlockFocus];
				}
				/********************************************************/

				ok = YES;
			}
		NX_HANDLER
		NX_ENDHANDLER
	}

	if (!ok)
	{
		if (!available)
			NXRunAlertPanel("Pattern", "Pattern dictionary not available.", "OK", NULL, NULL);
		else
			NXRunAlertPanel("Pattern", "Error while defining pattern", "OK", NULL, NULL);

		return nil;
	}

	return self;
}

/*************************************************************/
/*
*	The next two methods - image and compositeTo::
*	provide information and perform operations for
*	demonstration purposes.
*/
/*
*	This method returns the window holding the image.
*	It can be inserted into the window list to show how
*	the image is cached.
*/
- image
{
	return image;
}

/*
*	Composite the center of the pattern cell to the point
*	(x, y) in the currently focused view.
*/
- compositeTo:(NXCoord) x  :(NXCoord) y
{
	PSWCompositePattern(patternNum, patternImageNum, x, y);

	return self;
}
/*************************************************************/

- (BOOL) hasMatrix
{
	if (matrix)
		return YES;

	return NO;
}

- (const float *) matrix
{
	return matrix;
}

- (const char *) name
{
	return name;
}

- (int) patternNum
{
	return patternNum;
}

- (float) pointSize
{
	return size;
}

/*
 *	Place the pattern dictionary on top of the dictionary stack.
 *	The pattern dictionary contains redefinition of the drawing and
 *	showing operators to emulate the pattern operators in Level 2.
 *	This emulation turns patterns into fonts thereby taking advantage
 *	of the font cache.
 *
 *	The DrawingStatus variable performs the same function as the
 *	NXDrawingStatus variable except that the trigger is the
 *	composite-font radio buttons in the drawing options panel and
 *	not the actual drawing status.
 */
- set
{
	int		type, lock;

	/********************************************************/
	/*	Demonstration program only.                                */
	type = [[NXApp  typeMatrix]  selectedTag];
	lock = [[NXApp  lockMatrix]  selectedTag];
	/********************************************************/

	if (patternNum)
	{
		PSWBeginPattern();
		PSWSetPattern(patternNum);

		/*
		*	Only necessary if locking to view. The "lock == ..."
		*	checks are only for the demostration program.
		*/
		if (NXDrawingStatus == NX_DRAWING &&
			(lock == LOCK_VIEW || lock == LOCK_VIEWSCALE))
		{
			PSWBeginPatternView();
			if (lock == LOCK_VIEWSCALE)
				PSWBeginPatternViewScale();
		}

		/********************************************************/
		/*	Demonstration program only.                                */
		if (type == TYPE_DRAW)
		{
			PSWBeginPatternDraw();
		}
		else if (patternImageNum && type == TYPE_COMP)
		{
			PSWBeginPatternImage();
			PSWSetPatternImage(patternImageNum);
		}
		/********************************************************/

		/*
		*	Place a copy of userdict on the stack
		*/
		PSuserdict();
		PSbegin();
	}

	return self;
}

/*
 *	This method is necessary for emulation of the setpattern in Level 2.
 *	If the font method for patterns is being used, then this method removes
 *	the definitions that replace the regular drawing and showing operators.
 */
- unset
{
	int		type, lock;

	/********************************************************/
	/*	Demonstration program only.                                */
	type = [[NXApp  typeMatrix]  selectedTag];
	lock = [[NXApp  lockMatrix]  selectedTag];
	/********************************************************/

	if (patternNum)
	{
		PSend();				/* Userdict */

		/********************************************************/
		/*	Demonstration program only.                                */
		/*	PatternDrawDict or PatternImageDict */
		if (type == TYPE_DRAW || (patternImageNum && type == TYPE_COMP))
			PSend();
		/********************************************************/
		
					if (NXDrawingStatus == NX_DRAWING)
		/*
		*	If locking to view than pop the PatternViewDict off the
		*	dictionary stack and the PatternViewScaleDict if it is
		*	also on the stack. Once the locking type has been
		*	decided, the "lock == ..." checks are not necessary.
		*	Only the NXDrawingStatus check should remain.
		*/
		if (NXDrawingStatus == NX_DRAWING &&
			(lock == LOCK_VIEW || lock == LOCK_VIEWSCALE))
		{
			PSend();
			if (lock == LOCK_VIEWSCALE)
				PSend();
		}


		PSWUnsetPattern();
		PSend();				/* PatternDict */
	}

	return self;
}

@end
